package gojson

import (
	"fmt"
	"gitee.com/ymofen/gobase"
	"time"
)

type JSONBuilder struct {
	sb  gobase.BytesBuilder
	cnt int
}

type JSONDecode struct {
	sb         gobase.BytesBuilder
	lvl        int
	state      int // 0 search begin, 1, search end, 2: StatusString, 3: escape
	escapeflag byte
	beginChr   []byte
	endChr     []byte
}

func NewJSONDecode() *JSONDecode {
	rval := &JSONDecode{
		lvl:      0,
		state:    0,
		beginChr: make([]byte, 32),
		endChr:   make([]byte, 32),
	}
	return rval
}

func (this *JSONDecode) String() string {
	return this.sb.String()
}

func (this *JSONDecode) InputBuf(buf []byte, onjson func(jsonstr string)) {
	for i := 0; i < len(buf); i++ {
		if this.InputByte(buf[i]) == 1 {
			onjson(this.String())
		}
	}
}

func (this *JSONDecode) InputByte(val byte) (rval int) {
	rval = 0
	if this.state == -1 {
		this.sb.Reset()
		this.state = 0
		this.lvl = 0
		this.escapeflag = 0
	}

	if this.state == 0 || this.state == 1 {
		if val == '{' {
			if this.lvl >= len(this.beginChr) {
				this.state = -1
				return -1
			}
			this.beginChr[this.lvl] = '{'
			this.endChr[this.lvl] = '}'
			this.lvl++

			this.state = 1
			this.sb.WriteByte(val)
			return
		}
		if val == '[' {
			if this.lvl >= len(this.beginChr) {
				this.state = -1
				return -1
			}
			this.beginChr[this.lvl] = '['
			this.endChr[this.lvl] = ']'
			this.lvl++
			this.state = 1
			this.sb.WriteByte(val)
			return
		}

		if this.state == 0 { // 开始只能是 {, 或者[
			this.state = -1
			return -1
		}

		if val == '"' {
			this.state = 2
			this.sb.WriteByte(val)
			return 0
		}

		if val == this.endChr[this.lvl-1] {
			this.lvl--
			this.sb.WriteByte(val)
			if this.lvl == 0 {
				this.state = -1
				return 1
			}
			return 0
		}
		this.sb.WriteByte(val)
		return 0
	} else if this.state == 2 {
		if this.escapeflag == 1 {
			this.sb.WriteByte(val)
			this.escapeflag = 0
			return 0
		}

		if val == '\\' {
			this.sb.WriteByte(val)
			this.escapeflag = 1
			return 0
		}

		if val == '"' {
			this.sb.WriteByte(val)
			this.state = 1
			return 0
		}
		this.sb.WriteByte(val)
	}

	return rval
}

func BuildJSONString(kvPairs ...interface{}) string {
	jb := NewJSONBuilder()
	jb.AddObject(kvPairs...)
	return jb.String()
}

func BuildJSONBytes(kvPairs ...interface{}) []byte {
	jb := NewJSONBuilder()
	jb.AddObject(kvPairs...)
	return jb.Bytes()
}

func ToJSONValString(val interface{}) string {
	switch s := val.(type) {
	case time.Time:
		if s.IsZero() {
			return "\"\""
		} else {
			return fmt.Sprintf("\"%s\"", gobase.DateTimeString(s))
		}
	case string:
		return fmt.Sprintf("\"%s\"", gobase.EscapeJsonStrSimple(s))
	case int, int8, int16, int32, int64:
		return fmt.Sprintf("%d", s)
	case uint, uint8, uint16, uint32, uint64:
		return fmt.Sprintf("%d", s)
	case float32, float64:
		return fmt.Sprintf("%f", s)
	case bool:
		if s {
			return "true"
		} else {
			return "false"
		}
	case nil:
		return "null"
	default:
		istr, ok := val.(fmt.Stringer)
		if ok {
			return fmt.Sprintf("\"%s\"", istr.String())
		}
		return fmt.Sprintf("\"%v\"", s)
	}
}

func NewJSONBuilder() *JSONBuilder {
	return &JSONBuilder{}
}

func (this *JSONBuilder) ObjectBegin() *JSONBuilder {
	this.sb.WriteString("{")
	return this
}

func (this *JSONBuilder) ObjectEnd() *JSONBuilder {
	this.sb.WriteString("}")
	return this
}

func (this *JSONBuilder) ArrayBegin() *JSONBuilder {
	this.sb.WriteString("[")
	return this
}

func (this *JSONBuilder) ArrayEnd() *JSONBuilder {
	this.sb.WriteString("]")
	return this
}

func (this *JSONBuilder) Reset() *JSONBuilder {
	this.sb.Reset()
	this.cnt = 0
	return this
}

func (this *JSONBuilder) AddAny(strs ...string) *JSONBuilder {
	for _, v := range strs {
		this.sb.WriteString(v)
	}
	return this
}

func (this *JSONBuilder) AddObject(kvPairs ...interface{}) *JSONBuilder {
	this.ObjectBegin().AddValues(kvPairs...).ObjectEnd()
	return this
}

func (this *JSONBuilder) AddObjectEx(valfunc func(val interface{}) interface{}, kvPairs ...interface{}) *JSONBuilder {
	this.ObjectBegin().AddValuesEx(valfunc, kvPairs...).ObjectEnd()
	return this
}

func (this *JSONBuilder) AddKey(key string) *JSONBuilder {
	this.sb.WriteString("\"")
	this.sb.WriteString(key)
	this.sb.WriteString("\":")
	return this
}

func (this *JSONBuilder) Add(key string, val interface{}) *JSONBuilder {
	this.sb.WriteString("\"")
	this.sb.WriteString(key)
	this.sb.WriteString("\":")
	this.sb.WriteString(ToJSONValString(val))
	return this
}

func (this *JSONBuilder) AddValues(kvPairs ...interface{}) *JSONBuilder {
	l := len(kvPairs)
	if l%2 != 0 { // 长度必须是双数
		panic("kv Pairs 长度必须是双数")
	}
	j := 0
	i := 0
	for i < l {
		if key, ok := kvPairs[i].(string); ok {
			if j > 0 {
				this.Spliter()
			}
			this.Add(key, kvPairs[i+1])
			j++
		}
		i += 2
	}

	return this
}

func (this *JSONBuilder) EscapeSimpleVal(val interface{}) interface{} {
	if str, ok := val.(string); ok {
		return gobase.EscapeJsonStrSimple(str)
	} else {
		return val
	}
}

func (this *JSONBuilder) EscapeVal(val interface{}) interface{} {
	if str, ok := val.(string); ok {
		return gobase.EscapeJsonStr(str)
	} else {
		return val
	}
}

func (this *JSONBuilder) AddValuesEx(valfunc func(val interface{}) interface{}, kvPairs ...interface{}) *JSONBuilder {
	l := len(kvPairs)
	if l%2 != 0 { // 长度必须是双数
		panic("kv Pairs 长度必须是双数")
	}
	j := 0
	i := 0
	for i < l {
		if key, ok := kvPairs[i].(string); ok {
			if j > 0 {
				this.Spliter()
			}
			if valfunc != nil {
				this.Add(key, valfunc(kvPairs[i+1]))
			} else {
				this.Add(key, kvPairs[i+1])
			}

			j++
		}
		i += 2
	}

	return this
}

func (this *JSONBuilder) AddEscapeStr(key string, val string) *JSONBuilder {
	this.sb.WriteString("\"")
	this.sb.WriteString(key)
	this.sb.WriteString("\":")
	this.sb.WriteString("\"")
	this.sb.WriteString(gobase.EscapeJsonStr(val))
	this.sb.WriteString("\"")
	return this
}

func (this *JSONBuilder) Spliter() *JSONBuilder {
	this.sb.WriteString(",")
	return this
}

func (this *JSONBuilder) Length() int {
	return this.sb.Len()
}

func (this *JSONBuilder) String() string {
	return this.sb.String()
}

func (this *JSONBuilder) Bytes() []byte {
	return this.sb.Bytes()
}
